home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / MoreFiles 1.4.3 / Sources / Search.c < prev   
Encoding:
C/C++ Source or Header  |  1996-08-24  |  35.6 KB  |  1,231 lines  |  [TEXT/MPS ]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    IndexedSearch and the PBCatSearch compatibility function.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support Emeritus
  7. **
  8. **    File:        Search.c
  9. **
  10. **    Copyright © 1992-1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Types.h>
  23. #include <Gestalt.h>
  24. #include <Timer.h>
  25. #include <Errors.h>
  26. #include <Memory.h>
  27. #include <Files.h>
  28. #include <TextUtils.h>
  29.  
  30. #define    __COMPILINGMOREFILES
  31.  
  32. #include "MoreFiles.h"
  33. #include "MoreFilesExtras.h"
  34. #include "Search.h"
  35.  
  36. /*****************************************************************************/
  37.  
  38. enum
  39. {
  40.     /* Number of LevelRecs to add each time the searchStack is grown */
  41.     /* 20 levels is probably more than reasonable for most volumes. */
  42.     /* If more are needed, they are allocated 20 levels at a time. */
  43.     kAdditionalLevelRecs = 20
  44. };
  45.  
  46. /*****************************************************************************/
  47.  
  48. /*
  49. **    LevelRecs are used to store the directory ID and index whenever
  50. **    IndexedSearch needs to either scan a sub-directory, or return control
  51. **    to the caller because the call has timed out or the number of
  52. **    matches requested has been found. LevelRecs are stored in an array
  53. **    used as a stack.
  54. */
  55. struct    LevelRec
  56. {
  57.     long    dirModDate;    /* for detecting most (but not all) catalog changes */
  58.     long    dirID;
  59.     short    index;
  60. };
  61. typedef struct LevelRec LevelRec;
  62. typedef LevelRec *LevelRecPtr, **LevelRecHandle;
  63.  
  64.  
  65. /*
  66. **    SearchPositionRec is my version of a CatPositionRec. It holds the
  67. **    information I need to resuming searching.
  68. */
  69. #if PRAGMA_ALIGN_SUPPORTED
  70. #pragma options align=mac68k
  71. #endif
  72. struct SearchPositionRec
  73. {
  74.     long            initialize;        /* Goofy checksum of volume information used to make */
  75.                                     /* sure we're resuming a search on the same volume. */
  76.     unsigned short    stackDepth;        /* Current depth on searchStack. */
  77.     short            priv[11];        /* For future use... */
  78. };
  79. #if PRAGMA_ALIGN_SUPPORTED
  80. #pragma options align=reset
  81. #endif
  82. typedef struct SearchPositionRec SearchPositionRec;
  83. typedef SearchPositionRec *SearchPositionRecPtr;
  84.  
  85.  
  86. /*
  87. **    ExtendedTMTask is a TMTask record extended to hold the timer flag.
  88. */
  89. #if PRAGMA_ALIGN_SUPPORTED
  90. #pragma options align=mac68k
  91. #endif
  92. struct ExtendedTMTask
  93. {
  94.     TMTask            theTask;
  95.     Boolean            stopSearch;        /* the Time Mgr task will set stopSearch to */
  96.                                     /* true when the timer expires */
  97. };
  98. #if PRAGMA_ALIGN_SUPPORTED
  99. #pragma options align=reset
  100. #endif
  101. typedef struct ExtendedTMTask ExtendedTMTask;
  102. typedef ExtendedTMTask *ExtendedTMTaskPtr;
  103.  
  104. /*****************************************************************************/
  105.  
  106. static    OSErr    CheckVol(StringPtr pathname,
  107.                          short vRefNum,
  108.                          short *realVRefNum,
  109.                          long *volID);
  110.  
  111. static    OSErr    CheckStack(unsigned short stackDepth,
  112.                            LevelRecHandle searchStack,
  113.                            Size *searchStackSize);
  114.  
  115. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  116.                              Boolean *includeFiles,
  117.                              Boolean *includeDirs,
  118.                              Boolean *includeNames);
  119.  
  120. static    Boolean    IsSubString(StringPtr aStringPtr,
  121.                             StringPtr subStringPtr);
  122.  
  123. static    Boolean    CompareMasked(const long *data1,
  124.                               const long *data2,
  125.                               const long *mask,
  126.                               short longsToCompare);
  127.  
  128. static    void    CheckForMatches(CInfoPBPtr cPB,
  129.                                 CSParamPtr userPB,
  130.                                 const Str63 matchName,
  131.                                 Boolean includeFiles,
  132.                                 Boolean includeDirs);
  133.  
  134. #ifdef    __WANTPASCALELIMINATION
  135. #undef    pascal
  136. #endif
  137.  
  138. #if GENERATINGCFM
  139.  
  140. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr);
  141.  
  142. #else
  143.  
  144. static    pascal    TMTaskPtr    GetTMTaskPtr(void);
  145.  
  146. static    void    TimeOutTask(void);
  147.  
  148. #endif
  149.  
  150. #ifdef    __WANTPASCALELIMINATION
  151. #define    pascal    
  152. #endif
  153.  
  154. static    long    GetDirModDate(short vRefNum,
  155.                               long dirID);
  156.  
  157. /*****************************************************************************/
  158.  
  159. /*
  160. **    CheckVol gets the volume's real vRefNum and builds a volID. The volID
  161. **    is used to help insure that calls to resume searching with IndexedSearch
  162. **    are to the same volume as the last call to IndexedSearch.
  163. */
  164. static    OSErr    CheckVol(StringPtr pathname,
  165.                          short vRefNum,
  166.                          short *realVRefNum,
  167.                          long *volID)
  168. {
  169.     HParamBlockRec pb;
  170.     OSErr error;
  171.  
  172.     error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
  173.     if ( error == noErr )
  174.     {
  175.         /* Return the real vRefNum */
  176.         *realVRefNum = pb.volumeParam.ioVRefNum;
  177.  
  178.         /* Add together a bunch of things that aren't supposed to change on */
  179.         /* a mounted volume that's being searched and that should come up with */
  180.         /* a fairly unique number */
  181.         *volID = pb.volumeParam.ioVCrDate +
  182.                  pb.volumeParam.ioVRefNum +
  183.                  pb.volumeParam.ioVNmAlBlks +
  184.                  pb.volumeParam.ioVAlBlkSiz +
  185.                  pb.volumeParam.ioVFSID;
  186.     }
  187.     return ( error );
  188. }
  189.  
  190. /*****************************************************************************/
  191.  
  192. /*
  193. **    CheckStack checks the size of the search stack (array) to see if there's
  194. **    room to push another LevelRec. If not, CheckStack grows the stack by
  195. **    another kAdditionalLevelRecs elements.
  196. */
  197. static    OSErr    CheckStack(unsigned short stackDepth,
  198.                            LevelRecHandle searchStack,
  199.                            Size *searchStackSize)
  200. {
  201.     OSErr    result;
  202.     
  203.     if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
  204.     {
  205.         /* Time to grow stack */
  206.         SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
  207.         result = MemError();    /* should be noErr */
  208.         *searchStackSize = GetHandleSize((Handle)searchStack);
  209.     }
  210.     else
  211.     {
  212.         result = noErr;
  213.     }
  214.     
  215.     return ( result );
  216. }
  217.  
  218. /*****************************************************************************/
  219.  
  220. /*
  221. **    VerifyUserPB makes sure the parameter block passed to IndexedSearch has
  222. **    valid parameters. By making this check once, we don't have to worry about
  223. **    things like NULL pointers, strings being too long, etc.
  224. **    VerifyUserPB also determines if the search includes files and/or
  225. **    directories, and determines if a full or partial name search was requested.
  226. */
  227. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  228.                              Boolean *includeFiles,
  229.                              Boolean *includeDirs,
  230.                              Boolean *includeNames)
  231. {
  232.     CInfoPBPtr    searchInfo1;
  233.     CInfoPBPtr    searchInfo2;
  234.     
  235.     searchInfo1 = userPB->ioSearchInfo1;
  236.     searchInfo2 = userPB->ioSearchInfo2;
  237.     
  238.     /* ioMatchPtr cannot be NULL */
  239.     if ( userPB->ioMatchPtr == NULL )
  240.         goto ParamErrExit;
  241.     
  242.     /* ioSearchInfo1 cannot be NULL */
  243.     if ( searchInfo1 == NULL )
  244.         goto ParamErrExit;
  245.     
  246.     /* If any bits except partialName, fullName, or negate are set, then */
  247.     /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required  */
  248.     if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) &&
  249.          ( searchInfo2 == NULL ))
  250.         goto ParamErrExit;
  251.     
  252.     *includeFiles = false;
  253.     *includeDirs = false;
  254.     *includeNames = false;
  255.     
  256.     if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
  257.     {
  258.         /* If any kind of name matching is requested, then ioNamePtr in */
  259.         /* ioSearchInfo1 cannot be NULL or a zero-length string */
  260.         if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) ||
  261.              (searchInfo1->hFileInfo.ioNamePtr[0] == 0) ||
  262.              (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) )
  263.             goto ParamErrExit;
  264.         
  265.         *includeNames = true;
  266.     }
  267.     
  268.     if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
  269.     {
  270.         /* The only attributes you can search on are the directory flag */
  271.         /* and the locked flag. */
  272.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(ioDirMask | 0x01)) != 0 )
  273.             goto ParamErrExit;
  274.         
  275.         /* interested in the directory bit? */
  276.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  277.         {
  278.             /* yes, so do they want just directories or just files? */
  279.             if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  280.                 *includeDirs = true;
  281.             else
  282.                 *includeFiles = true;
  283.         }
  284.         else
  285.         {
  286.             /* no interest in directory bit - get both files and directories */
  287.             *includeDirs = true;
  288.             *includeFiles = true;
  289.         }
  290.     }
  291.     else
  292.     {
  293.         /* no attribute checking - get both files and directories */
  294.         *includeDirs = true;
  295.         *includeFiles = true;
  296.     }
  297.     
  298.     /* If directories are included in the search, */
  299.     /* then the locked attribute cannot be requested. */
  300.     if ( *includeDirs &&
  301.          ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
  302.          ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) )
  303.         goto ParamErrExit;
  304.     
  305.     /* If files are included in the search, then there cannot be */
  306.     /* a search on the number of files. */
  307.     if ( *includeFiles &&
  308.          ((userPB->ioSearchBits & fsSBDrNmFls) != 0) )
  309.         goto ParamErrExit;
  310.     
  311.     /* If directories are included in the search, then there cannot */
  312.     /* be a search on file lengths. */
  313.     if ( *includeDirs &&
  314.          ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
  315.         goto ParamErrExit;
  316.     
  317.     return ( noErr );
  318.          
  319. ParamErrExit:
  320.     return ( paramErr );
  321. }
  322.  
  323. /*****************************************************************************/
  324.  
  325. /*
  326. **    IsSubString checks to see if a string is a substring of another string.
  327. **    Both input strings have already been converted to all uppercase using
  328. **    UprString (the same non-international call the File Manager uses).
  329. */
  330. static    Boolean    IsSubString(StringPtr aStringPtr,
  331.                             StringPtr subStringPtr)
  332. {
  333.     short    strLength;        /* length of string */
  334.     short    subStrLength;    /* length of subString */
  335.     Boolean    found;            /* result of test */
  336.     short    index;            /* current index into string */
  337.     
  338.     found = false;
  339.     strLength = aStringPtr[0];
  340.     subStrLength = subStringPtr[0];
  341.         
  342.     if ( subStrLength <= strLength)
  343.     {
  344.         register short    count;            /* search counter */
  345.         register short    strIndex;        /* running index into string */
  346.         register short    subStrIndex;    /* running index into subString */
  347.         
  348.         /* start looking at first character */
  349.         index = 1;
  350.         
  351.         /* continue looking until remaining string is shorter than substring */
  352.         count = strLength - subStrLength + 1;
  353.         
  354.         do
  355.         {
  356.             strIndex = index;    /* start string index at index */
  357.             subStrIndex = 1;    /* start subString index at 1 */
  358.             
  359.             while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) )
  360.             {
  361.                 if ( subStrIndex == subStrLength )
  362.                 {
  363.                     /* all characters in subString were found */
  364.                     found = true;
  365.                 }
  366.                 else
  367.                 {
  368.                     /* check next character of substring against next character of string */
  369.                     ++subStrIndex;
  370.                     ++strIndex;
  371.                 }
  372.             }
  373.             
  374.             if ( !found )
  375.             {
  376.                 /* start substring search again at next string character */
  377.                 ++index;
  378.                 --count;
  379.             }
  380.         } while ( count != 0 && (!found) );
  381.     }
  382.     
  383.     return ( found );
  384. }
  385.  
  386. /*****************************************************************************/
  387.  
  388. /*
  389. **    CompareMasked does a bitwise comparison with mask on 1 or more longs.
  390. **    data1 and data2 are first exclusive-ORed together resulting with bits set
  391. **    where they are different. That value is then ANDed with the mask resulting
  392. **    with bits set if the test fails. true is returned if the tests pass.
  393. */
  394. static    Boolean    CompareMasked(const long *data1,
  395.                               const long *data2,
  396.                               const long *mask,
  397.                               short longsToCompare)
  398. {
  399.     Boolean    result = true;
  400.     
  401.     while ( (longsToCompare != 0) && (result == true) )
  402.     {
  403.         /* (*data1 ^ *data2) = bits that are different, so... */
  404.         /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
  405.         
  406.         if ( ((*data1 ^ *data2) & *mask) != 0 )
  407.             result = false;
  408.         
  409.         ++data1;
  410.         ++data2;
  411.         ++mask;
  412.         --longsToCompare;
  413.     }
  414.     
  415.     return ( result );
  416. }
  417.  
  418. /*****************************************************************************/
  419.  
  420. /*
  421. **    Check for matches compares the search criteria in userPB to the file
  422. **    system object in cPB. If there's a match, then the information in cPB is
  423. **    is added to the match array and the actual match count is incremented.
  424. */
  425. static    void    CheckForMatches(CInfoPBPtr cPB,
  426.                                 CSParamPtr userPB,
  427.                                 const Str63 matchName,
  428.                                 Boolean includeFiles,
  429.                                 Boolean includeDirs)
  430. {
  431.     long        searchBits;
  432.     CInfoPBPtr    searchInfo1;
  433.     CInfoPBPtr    searchInfo2;
  434.     Str63        itemName;        /* copy of object's name for partial name matching */
  435.     Boolean        foundMatch;
  436.     
  437.     foundMatch = false;            /* default to no match */
  438.     
  439.     searchBits = userPB->ioSearchBits;
  440.     searchInfo1 = userPB->ioSearchInfo1;
  441.     searchInfo2 = userPB->ioSearchInfo2;
  442.     
  443.     /* Into the if statements that go on forever... */
  444.     
  445.     if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 )
  446.     {
  447.         if (!includeFiles)
  448.             goto Failed;
  449.     }
  450.     else
  451.     {
  452.         if (!includeDirs)
  453.             goto Failed;
  454.     }
  455.     
  456.     if ( (searchBits & fsSBPartialName) != 0 )
  457.     {
  458.         if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
  459.              (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
  460.         {
  461.             /* Make uppercase copy of object name */
  462.             BlockMoveData(cPB->hFileInfo.ioNamePtr,
  463.                             itemName,
  464.                             cPB->hFileInfo.ioNamePtr[0] + 1);
  465.             /* Use the same non-international call the File Manager uses */
  466.             UpperString(itemName, true);
  467.         }
  468.         else
  469.         {
  470.             goto Failed;
  471.         }
  472.         
  473.         {
  474.             if ( !IsSubString(itemName, (StringPtr)matchName) )
  475.             {
  476.                 goto Failed;
  477.             }
  478.             else if ( searchBits == fsSBPartialName )
  479.             {
  480.                 /* optimize for name matching only since it is most common way to search */
  481.                 goto Hit;
  482.             }
  483.         }
  484.     }
  485.     
  486.     if ( (searchBits & fsSBFullName) != 0 )
  487.     {
  488.         /* Use the same non-international call the File Manager uses */
  489.         if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
  490.         {
  491.             goto Failed;
  492.         }
  493.         else if ( searchBits == fsSBFullName )
  494.         {
  495.             /* optimize for name matching only since it is most common way to search */
  496.             goto Hit;
  497.         }
  498.     }
  499.     
  500.     if ( (searchBits & fsSBFlParID) != 0 )
  501.     {
  502.         if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
  503.              ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
  504.         {
  505.             goto Failed;
  506.         }
  507.     }
  508.     
  509.     if ( (searchBits & fsSBFlAttrib) != 0 )
  510.     {
  511.         if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
  512.               searchInfo2->hFileInfo.ioFlAttrib) != 0 )
  513.         {
  514.             goto Failed;
  515.         }
  516.     }
  517.     
  518.     if ( (searchBits & fsSBDrNmFls) != 0 )
  519.     {
  520.         if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
  521.              ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
  522.         {
  523.             goto Failed;
  524.         }
  525.     }
  526.  
  527.     if ( (searchBits & fsSBFlFndrInfo) != 0 )    /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
  528.     {
  529.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
  530.                             (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
  531.                             (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
  532.                             sizeof(FInfo) / sizeof(long)) )
  533.         {
  534.             goto Failed;
  535.         }
  536.     }
  537.     
  538.     if ( (searchBits & fsSBFlXFndrInfo) != 0 )    /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
  539.     {
  540.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
  541.                             (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
  542.                             (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
  543.                             sizeof(FXInfo) / sizeof(long)) )
  544.         {
  545.             goto Failed;
  546.         }
  547.     }
  548.     
  549.     if ( (searchBits & fsSBFlLgLen) != 0 )
  550.     {
  551.         if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
  552.              ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
  553.         {
  554.             goto Failed;
  555.         }
  556.     }
  557.  
  558.     if ( (searchBits & fsSBFlPyLen) != 0 )
  559.     {
  560.         if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
  561.              ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
  562.         {
  563.             goto Failed;
  564.         }
  565.     }
  566.  
  567.     if ( (searchBits & fsSBFlRLgLen) != 0 )
  568.     {
  569.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
  570.              ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
  571.         {
  572.             goto Failed;
  573.         }
  574.     }
  575.  
  576.     if ( (searchBits & fsSBFlRPyLen) != 0 )
  577.     {
  578.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
  579.              ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
  580.         {
  581.             goto Failed;
  582.         }
  583.     }
  584.  
  585.     if ( (searchBits & fsSBFlCrDat) != 0 )    /* fsSBFlCrDat is same as fsSBDrCrDat */
  586.     {
  587.         if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
  588.              ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
  589.         {
  590.             goto Failed;
  591.         }
  592.     }
  593.  
  594.     if ( (searchBits & fsSBFlMdDat) != 0 )    /* fsSBFlMdDat is same as fsSBDrMdDat */
  595.     {
  596.         if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
  597.              ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
  598.         {
  599.             goto Failed;
  600.         }
  601.     }
  602.  
  603.     if ( (searchBits & fsSBFlBkDat) != 0 )    /* fsSBFlBkDat is same as fsSBDrBkDat */
  604.     {
  605.         if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
  606.              ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
  607.         {
  608.             goto Failed;
  609.         }
  610.     }
  611.  
  612.     /* Hey, we passed all of the tests! */
  613.     
  614. Hit:
  615.     foundMatch = true;
  616.  
  617. /* foundMatch is false if code jumps to Failed */
  618. Failed:
  619.     /* Do we reverse our findings? */
  620.     if ( (searchBits & fsSBNegate) != 0 )
  621.         foundMatch = !foundMatch;    /* matches are not, not matches are */
  622.     
  623.     if ( foundMatch )
  624.     {
  625.  
  626.         /* Move the match into the match buffer */
  627.         userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum;
  628.         userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID;
  629.         if ( cPB->hFileInfo.ioNamePtr[0] > 63 )
  630.             cPB->hFileInfo.ioNamePtr[0] = 63;
  631.         BlockMoveData(cPB->hFileInfo.ioNamePtr,
  632.                       userPB->ioMatchPtr[userPB->ioActMatchCount].name,
  633.                       cPB->hFileInfo.ioNamePtr[0] + 1);
  634.         
  635.         /* increment the actual count */
  636.         ++(userPB->ioActMatchCount);
  637.     }
  638. }
  639.  
  640. /*****************************************************************************/
  641.  
  642. /*
  643. **    TimeOutTask is executed when the timer goes off. It simply sets the
  644. **    stopSearch field to true. After each object is found and possibly added
  645. **    to the matches buffer, stopSearch is checked to see if the search should
  646. **    continue.
  647. */
  648.  
  649. #ifdef    __WANTPASCALELIMINATION
  650. #undef    pascal
  651. #endif
  652.  
  653. #if GENERATINGCFM
  654.  
  655. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr)
  656. {
  657.     ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
  658. }
  659.  
  660. #else
  661.  
  662. static    pascal    TMTaskPtr    GetTMTaskPtr(void)
  663.     ONEWORDINLINE(0x2e89);    /* MOVE.L A1,(SP) */
  664.     
  665. static    void    TimeOutTask(void)
  666. {
  667.     ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
  668. }
  669.  
  670. #endif
  671.  
  672. #ifdef    __WANTPASCALELIMINATION
  673. #define    pascal    
  674. #endif
  675.  
  676. /*****************************************************************************/
  677.  
  678. /*
  679. **    GetDirModDate returns the modification date of a directory. If there is
  680. **    an error getting the modification date, -1 is returned to indicate
  681. **    something went wrong.
  682. */
  683. static    long    GetDirModDate(short vRefNum,
  684.                               long dirID)
  685. {
  686.     CInfoPBRec pb;
  687.     Str31 tempName;
  688.     long modDate;
  689.  
  690.     /* Protection against File Sharing problem */
  691.     tempName[0] = 0;
  692.     pb.dirInfo.ioNamePtr = tempName;
  693.     pb.dirInfo.ioVRefNum = vRefNum;
  694.     pb.dirInfo.ioDrDirID = dirID;
  695.     pb.dirInfo.ioFDirIndex = -1;    /* use ioDrDirID */
  696.     
  697.     if ( PBGetCatInfoSync(&pb) == noErr )
  698.     {
  699.         modDate = pb.dirInfo.ioDrMdDat;
  700.     }
  701.     else
  702.     {
  703.         modDate = -1;
  704.     }
  705.     
  706.     return ( modDate );
  707. }
  708.  
  709. /*****************************************************************************/
  710.  
  711. pascal    OSErr    IndexedSearch(CSParamPtr pb,
  712.                               long dirID)
  713. {
  714.     static LevelRecHandle    searchStack = NULL;        /* static handle to LevelRec stack */
  715.     static Size                searchStackSize = 0;    /* size of static handle */
  716.     SearchPositionRecPtr    catPosition;
  717.     long                    modDate;
  718.     short                    index;
  719.     ExtendedTMTask            timerTask;
  720.     OSErr                    result;
  721.     short                    realVRefNum;
  722.     Str63                    itemName;
  723.     CInfoPBRec                cPB;
  724.     long                    tempLong;
  725.     Boolean                    includeFiles;
  726.     Boolean                    includeDirs;
  727.     Boolean                    includeNames;
  728.     Str63                    upperName;
  729.     
  730.     timerTask.stopSearch = false;    /* don't stop yet! */
  731.     
  732.     /* If request has a timeout, install a Time Manager task. */
  733.     if ( pb->ioSearchTime != 0 )
  734.     {
  735.         /* Start timer */
  736.         timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
  737.         InsTime((QElemPtr)&(timerTask.theTask));
  738.         PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
  739.     }
  740.     
  741.     /* Check the parameter block passed for things that we don't want to assume */
  742.     /* are OK later in the code. For example, make sure pointers to data structures */
  743.     /* and buffers are not NULL.  And while we're in there, see if the request */
  744.     /* specified searching for files, directories, or both, and see if the search */
  745.     /* was by full or partial name. */
  746.     result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames);
  747.     if ( result == noErr )
  748.     {
  749.         pb->ioActMatchCount = 0;    /* no matches yet */
  750.     
  751.         if ( includeNames )
  752.         {
  753.             /* The search includes seach by full or partial name. */
  754.             /* Make an upper case copy of the match string to pass to */
  755.             /* CheckForMatches. */
  756.             BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr,
  757.                             upperName,
  758.                             pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
  759.             /* Use the same non-international call the File Manager uses */
  760.             UpperString(upperName, true);
  761.         }
  762.         
  763.         /* Prevent casting to my type throughout code */
  764.         catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
  765.         
  766.         /* Create searchStack first time called */
  767.         if ( searchStack == NULL )
  768.         {
  769.             searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
  770.         }
  771.         
  772.         /* Make sure searchStack really exists */
  773.         if ( searchStack != NULL )
  774.         {
  775.             searchStackSize = GetHandleSize((Handle)searchStack);
  776.             
  777.             /* See if the search is a new search or a resumed search. */
  778.             if ( catPosition->initialize == 0 )
  779.             {
  780.                 /* New search. */
  781.                 
  782.                 /* Get the real vRefNum and fill in catPosition->initialize. */ 
  783.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
  784.                 if ( result == noErr )
  785.                 {
  786.                     /* clear searchStack */
  787.                     catPosition->stackDepth = 0;
  788.                     
  789.                     /* use dirID parameter passed and... */
  790.                     index = -1;    /* start with the passed directory itself! */
  791.                 }
  792.             }
  793.             else
  794.             {
  795.                 /* We're resuming a search. */
  796.     
  797.                 /* Get the real vRefNum and make sure catPosition->initialize is valid. */ 
  798.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong);
  799.                 if ( result == noErr )
  800.                 {
  801.                     /* Make sure the resumed search is to the same volume! */
  802.                     if ( catPosition->initialize == tempLong )
  803.                     {
  804.                         /* For resume, catPosition->stackDepth > 0 */
  805.                         if ( catPosition->stackDepth > 0 )
  806.                         {
  807.                             /* Position catPosition->stackDepth to access last saved level */
  808.                             --(catPosition->stackDepth);
  809.             
  810.                             /* Get the dirID and index for the next item */
  811.                             dirID = (*searchStack)[catPosition->stackDepth].dirID;
  812.                             index = (*searchStack)[catPosition->stackDepth].index;
  813.                             
  814.                             /* Check the dir's mod date against the saved mode date on our "stack" */
  815.                             modDate = GetDirModDate(realVRefNum, dirID);
  816.                             if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  817.                                 result = catChangedErr;
  818.                         }
  819.                         else
  820.                         {
  821.                             /* Invalid catPosition record was passed */
  822.                             result = paramErr;
  823.                         }
  824.                     }
  825.                     else
  826.                     {
  827.                         /* The volume is not the same */
  828.                         result = catChangedErr;
  829.                     }
  830.                 }
  831.             }
  832.             
  833.             if ( result == noErr )
  834.             {
  835.                 /* ioNamePtr and ioVRefNum only need to be set up once. */
  836.                 cPB.hFileInfo.ioNamePtr = itemName;
  837.                 cPB.hFileInfo.ioVRefNum = realVRefNum;
  838.                 
  839.                 /*
  840.                 **    Here's the loop that:
  841.                 **        Finds the next item on the volume.
  842.                 **        If noErr, calls the code to check for matches and add matches
  843.                 **            to the match buffer.
  844.                 **        Sets up dirID and index for to find the next item on the volume.
  845.                 **
  846.                 **    The looping ends when:
  847.                 **        (a) an unexpected error is returned by PBGetCatInfo. All that
  848.                 **            is expected is noErr and fnfErr (after the last item in a
  849.                 **            directory is found).
  850.                 **        (b) the caller specified a timeout and our Time Manager task
  851.                 **            has fired.
  852.                 **        (c) the number of matches requested by the caller has been found.
  853.                 **        (d) the last item on the volume was found.
  854.                 */
  855.                 do
  856.                 {
  857.                     /* get the next item */
  858.                     cPB.hFileInfo.ioFDirIndex = index;
  859.                     cPB.hFileInfo.ioDirID = dirID;
  860.                     result = PBGetCatInfoSync(&cPB);
  861.                     if ( index != -1 )
  862.                     {
  863.                         if ( result == noErr )
  864.                         {
  865.                             /* We found something */
  866.         
  867.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  868.                             
  869.                             ++index;
  870.                             if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  871.                             {
  872.                                 /* It's a directory */
  873.                                 
  874.                                 result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  875.                                 if ( result == noErr )
  876.                                 {
  877.                                     /* Save the current state on the searchStack */
  878.                                     /* when we come back, this is where we'll start */
  879.                                     (*searchStack)[catPosition->stackDepth].dirID = dirID;
  880.                                     (*searchStack)[catPosition->stackDepth].index = index;
  881.                                     (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  882.                                     
  883.                                     /* position catPosition->stackDepth for next saved level */
  884.                                     ++(catPosition->stackDepth);
  885.                                     
  886.                                     /* The next item to get is the 1st item in the child directory */
  887.                                     dirID = cPB.dirInfo.ioDrDirID;
  888.                                     index = 1;
  889.                                 }
  890.                             }
  891.                             /* else do nothing for files */
  892.                         }
  893.                         else
  894.                         {
  895.                             /* End of directory found (or we had some error and that */
  896.                             /* means we have to drop out of this directory). */
  897.                             /* Restore last thing put on stack and */
  898.                             /* see if we need to continue or quit. */
  899.                             if ( catPosition->stackDepth > 0 )
  900.                             {
  901.                                 /* position catPosition->stackDepth to access last saved level */
  902.                                 --(catPosition->stackDepth);
  903.                                 
  904.                                 dirID = (*searchStack)[catPosition->stackDepth].dirID;
  905.                                 index = (*searchStack)[catPosition->stackDepth].index;
  906.                                 
  907.                                 /* Check the dir's mod date against the saved mode date on our "stack" */
  908.                                 modDate = GetDirModDate(realVRefNum, dirID);
  909.                                 if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  910.                                 {
  911.                                     result = catChangedErr;
  912.                                 }
  913.                                 else
  914.                                 {
  915.                                     /* Going back to ancestor directory. */
  916.                                     /* Clear error so we can continue. */
  917.                                     result = noErr;
  918.                                 }
  919.                             }
  920.                             else
  921.                             {
  922.                                 /* We hit the bottom of the stack, so we'll let the */
  923.                                 /* the eofErr drop us out of the loop. */
  924.                                 result = eofErr;
  925.                             }
  926.                         }
  927.                     }
  928.                     else
  929.                     {
  930.                         /* Special case for index == -1; that means that we're starting */
  931.                         /* a new search and so the first item to check is the directory */
  932.                         /* passed to us. */
  933.                         if ( result == noErr )
  934.                         {
  935.                             /* We found something */
  936.         
  937.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  938.                             
  939.                             /* Now, set the index to 1 and then we're ready to look inside */
  940.                             /* the passed directory. */
  941.                             index = 1;
  942.                         }
  943.                     }
  944.                 } while ( (!timerTask.stopSearch) &&    /* timer hasn't fired */
  945.                           (result == noErr) &&            /* no unexpected errors */
  946.                           (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */
  947.                 
  948.                 /* Did we drop out of the loop because of timeout or */
  949.                 /* ioReqMatchCount was found? */
  950.                 if ( result == noErr )
  951.                 {
  952.                     result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  953.                     if ( result == noErr )
  954.                     {
  955.                         /* Either there was a timeout or ioReqMatchCount was reached. */
  956.                         /* Save the dirID and index for the next time we're called. */
  957.                         
  958.                         (*searchStack)[catPosition->stackDepth].dirID = dirID;
  959.                         (*searchStack)[catPosition->stackDepth].index = index;
  960.                         (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  961.                         
  962.                         /* position catPosition->stackDepth for next saved level */
  963.                         
  964.                         ++(catPosition->stackDepth);
  965.                     }
  966.                 }
  967.             }
  968.         }
  969.         else
  970.         {
  971.             /* searchStack Handle could not be allocated */
  972.             result = memFullErr;
  973.         }
  974.     }
  975.     
  976.     if ( pb->ioSearchTime != 0 )
  977.     {
  978.         /* Stop Time Manager task here if it was installed */
  979.         RmvTime((QElemPtr)&(timerTask.theTask));
  980.         DisposeRoutineDescriptor(timerTask.theTask.tmAddr);
  981.     }
  982.     
  983.     return ( result );
  984. }
  985.  
  986. /*****************************************************************************/
  987.  
  988. pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
  989. {
  990.     static Boolean            fullExtFSDispatchingtested = false;
  991.     static Boolean            hasFullExtFSDispatching = false;
  992.     OSErr                     result;
  993.     Boolean                    supportsCatSearch;
  994.     long                    response;
  995.     GetVolParmsInfoBuffer    volParmsInfo;
  996.     long                    infoSize;
  997.     
  998.     result = noErr;
  999.  
  1000.     /* See if File Manager will pass CatSearch requests to external file systems */
  1001.     /* we'll store the results in a static variable so we don't have to call Gestalt */
  1002.     /* everytime we're called. */
  1003.     if ( !fullExtFSDispatchingtested )
  1004.     {
  1005.         fullExtFSDispatchingtested = true;
  1006.         if ( Gestalt(gestaltFSAttr, &response) == noErr )
  1007.         {
  1008.             hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
  1009.         }
  1010.     }
  1011.     
  1012.     /* CatSearch is a per volume attribute, so we have to check each time we're */
  1013.     /* called to see if it is available on the volume specified. */
  1014.     supportsCatSearch = false;
  1015.     if ( hasFullExtFSDispatching )
  1016.     {
  1017.         infoSize = sizeof(GetVolParmsInfoBuffer);
  1018.         result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
  1019.                             &volParmsInfo, &infoSize);
  1020.         if ( result == noErr )
  1021.         {
  1022.             supportsCatSearch = hasCatSearch(volParmsInfo);
  1023.         }
  1024.     }
  1025.     
  1026.     /* noErr or paramErr is OK here. */
  1027.     /* paramErr just means that GetVolParms isn't supported by this volume */
  1028.     if ( (result == noErr) || (result == paramErr) )
  1029.     {
  1030.         if ( supportsCatSearch )
  1031.         {
  1032.             /* Volume supports CatSearch so use it. */
  1033.             /* CatSearch is faster than an indexed search. */
  1034.             result = PBCatSearchSync(paramBlock);
  1035.         }
  1036.         else
  1037.         {
  1038.             /* Volume doesn't support CatSearch so */
  1039.             /* search using IndexedSearch from root directory. */
  1040.             result = IndexedSearch(paramBlock, fsRtDirID);
  1041.         }
  1042.     }
  1043.     
  1044.     return ( result );
  1045. }
  1046.  
  1047. /*****************************************************************************/
  1048.  
  1049. pascal    OSErr    NameFileSearch(StringPtr volName,
  1050.                                short vRefNum,
  1051.                                ConstStr255Param fileName,
  1052.                                FSSpecPtr matches,
  1053.                                long reqMatchCount,
  1054.                                long *actMatchCount,
  1055.                                Boolean newSearch,
  1056.                                Boolean partial)
  1057. {
  1058.     CInfoPBRec        searchInfo1, searchInfo2;
  1059.     HParamBlockRec    pb;
  1060.     OSErr            error;
  1061.     static CatPositionRec catPosition;
  1062.     static short    lastVRefNum = 0;
  1063.     
  1064.     /* get the real volume reference number */
  1065.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1066.     if ( error != noErr )
  1067.         return ( error );
  1068.     
  1069.     pb.csParam.ioNamePtr = NULL;
  1070.     pb.csParam.ioVRefNum = vRefNum;
  1071.     pb.csParam.ioMatchPtr = matches;
  1072.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1073.     pb.csParam.ioSearchBits = ( partial ) ?        /* tell CatSearch what we're looking for: */
  1074.         ( fsSBPartialName + fsSBFlAttrib ) :    /* partial name file matches or */
  1075.         ( fsSBFullName + fsSBFlAttrib );        /* full name file matches */
  1076.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1077.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1078.     pb.csParam.ioSearchTime = 0;
  1079.     if ( (newSearch) ||                /* If caller specified new search */
  1080.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1081.     {
  1082.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1083.     }
  1084.     pb.csParam.ioCatPosition = catPosition;
  1085.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1086.  
  1087.     /* search for fileName */
  1088.     searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
  1089.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1090.     
  1091.     /* only match files (not directories) */
  1092.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1093.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1094.  
  1095.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1096.     
  1097.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1098.          (error == eofErr) )                            /* found, then the call was successful so */
  1099.     {
  1100.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1101.     }
  1102.     else
  1103.     {
  1104.         *actMatchCount = 0;                            /* else no matches found */
  1105.     }
  1106.     
  1107.     if ( (error == noErr) ||                        /* If no errors */
  1108.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1109.     {
  1110.         catPosition = pb.csParam.ioCatPosition;
  1111.         lastVRefNum = vRefNum;
  1112.             /* we can probably start the next search where we stopped this time */
  1113.     }
  1114.     else
  1115.     {
  1116.         catPosition.initialize = 0;
  1117.             /* start the next search from beginning of catalog */
  1118.     }
  1119.     
  1120.     if ( pb.csParam.ioOptBuffer != NULL )
  1121.     {
  1122.         DisposePtr(pb.csParam.ioOptBuffer);
  1123.     }
  1124.         
  1125.     return ( error );
  1126. }
  1127.  
  1128. /*****************************************************************************/
  1129.  
  1130. pascal    OSErr    CreatorTypeFileSearch(StringPtr volName,
  1131.                                       short vRefNum,
  1132.                                       OSType creator,
  1133.                                       OSType fileType,
  1134.                                       FSSpecPtr matches,
  1135.                                       long reqMatchCount,
  1136.                                       long *actMatchCount,
  1137.                                       Boolean newSearch)
  1138. {
  1139.     CInfoPBRec        searchInfo1, searchInfo2;
  1140.     HParamBlockRec    pb;
  1141.     OSErr            error;
  1142.     static CatPositionRec catPosition;
  1143.     static short    lastVRefNum = 0;
  1144.     
  1145.     /* get the real volume reference number */
  1146.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1147.     if ( error != noErr )
  1148.         return ( error );
  1149.     
  1150.     pb.csParam.ioNamePtr = NULL;
  1151.     pb.csParam.ioVRefNum = vRefNum;
  1152.     pb.csParam.ioMatchPtr = matches;
  1153.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1154.     pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo;    /* Looking for finder info file matches */
  1155.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1156.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1157.     pb.csParam.ioSearchTime = 0;
  1158.     if ( (newSearch) ||                /* If caller specified new search */
  1159.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1160.     {
  1161.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1162.     }
  1163.     pb.csParam.ioCatPosition = catPosition;
  1164.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1165.  
  1166.     /* no fileName */
  1167.     searchInfo1.hFileInfo.ioNamePtr = NULL;
  1168.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1169.     
  1170.     /* only match files (not directories) */
  1171.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1172.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1173.     
  1174.     /* search for creator; if creator = 0x00000000, ignore creator */
  1175.     searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  1176.     searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = ( creator == (OSType)0x00000000 ) ?
  1177.         (OSType)0x00000000 :
  1178.         (OSType)0xffffffff;
  1179.     
  1180.     /* search for fileType; if fileType = 0x00000000, ignore fileType */
  1181.     searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
  1182.     searchInfo2.hFileInfo.ioFlFndrInfo.fdType = ( fileType == (OSType)0x00000000 ) ?
  1183.         (OSType)0x00000000 :
  1184.         (OSType)0xffffffff;
  1185.     
  1186.     /* zero all other FInfo fields */
  1187.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1188.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1189.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1190.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1191.     
  1192.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1193.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1194.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1195.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1196.  
  1197.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1198.     
  1199.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1200.          (error == eofErr) )                            /* found, then the call was successful so */
  1201.     {
  1202.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1203.     }
  1204.     else
  1205.     {
  1206.         *actMatchCount = 0;                            /* else no matches found */
  1207.     }
  1208.     
  1209.     if ( (error == noErr) ||                        /* If no errors */
  1210.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1211.     {
  1212.         catPosition = pb.csParam.ioCatPosition;
  1213.         lastVRefNum = vRefNum;
  1214.             /* we can probably start the next search where we stopped this time */
  1215.     }
  1216.     else
  1217.     {
  1218.         catPosition.initialize = 0;
  1219.             /* start the next search from beginning of catalog */
  1220.     }
  1221.     
  1222.     if ( pb.csParam.ioOptBuffer != NULL )
  1223.     {
  1224.         DisposePtr(pb.csParam.ioOptBuffer);
  1225.     }
  1226.         
  1227.     return ( error );
  1228. }
  1229.  
  1230. /*****************************************************************************/
  1231.